import { Injectable } from '@angular/core';
import { StringUtilsService } from './string-utils.service';

@Injectable({
  providedIn: 'root'
})
export class ArrayutilsService {

  constructor(
    private stringUtils:StringUtilsService
  ) { }

  //#region 二维数组生成树形结构用到的全局属性
  levels = [];
  treeNodeKeyFieldName = 'auto_key';
  //#endregion

  //#region 二维数组生成树形结构
  /**
   * 将二维数组转换为树形结构。将下面SQL语句获得的集合转换为按照：数据库、表名、字段名，分3层的树形结构
   * 实际这里不限制层数，按照第二个参数中数组的长度动态分层，本案例以ng-zorro-antd 的 nzTree 为例制作
   * 如果更换为其他组件，那么修改方法 recursion 中的构建 node 的属性即可
   * @param rs 二维数组
   * @param distFields 按照从上到下分层次的字段构成的字符串数组
   * @param keyFieldName 二维数组中主键字段名称，可唯一确定一行数据
   */
  buildTree(rs:any[],distFields:string[],keyFieldName:string):any[]{
    let i = 0;
    for(i=0;i<distFields.length;i++){
      let ele = [this.buildDistArray(rs,distFields.slice(0,i+1)),distFields.slice(0,i+1)];
      this.levels.push(ele);
    }

    if( keyFieldName.length>0 ) this.treeNodeKeyFieldName = keyFieldName;
    return this.recursion(0,this.levels[0][0],this.levels[0][1]);
  }

  private recursion(level:number,curLevelArray:any[],distFields:string[]):any[]{
    let nodes = [];
    curLevelArray.forEach(ele => {
      let distField = distFields[distFields.length-1];
      let title = ele[distField];
      let node = {'title':title,'key':ele[this.treeNodeKeyFieldName],'children':[],'isLeaf':false};
      if( level===this.levels.length-1 ) node.isLeaf = true;

      let nextLevel = level + 1;
      let childrenNodes = [];
      if(nextLevel<this.levels.length){
        let subArray = this.getSames(this.levels[nextLevel][0],ele,this.levels[level][1]);
        childrenNodes = this.recursion(nextLevel,subArray,this.levels[nextLevel][1]);
      }
      node.children = childrenNodes;
      nodes.push(node);
    });

    return nodes;
  }
  //#endregion

  /**
   * 检测某个元素是否存在于数组中，存在则返回true
   * @param array 被检测的数组
   * @param ele 被检测的元素
   */
  has(array:any[],ele:any):boolean{
    return array.some((element,index,array)=>{
      return ele === element;
    });
  }

  /**
   * 检测数组中是否有类似的元素，检测指定的几个属性的值都相等，则返回true
   * 要求数组中元素的结构和样本对象的结构相同
   * @param array 被检测的数组
   * @param ele 样本对象，带有N个属性
   * @param fields 要核对的N个属性名构成的字符串数组
   */
  hasSame(array:any[],ele:any,fields:string[]):boolean{
    let sample = this.combineAttr2String(ele,fields);
    console.log('样本：' + sample);

    return array.some(
      (item,index,array)=>{
        let detected = this.combineAttr2String(item,fields);
        console.log('检测对象：' + detected);
        return sample === detected;
      }
    )
  }

  /**
   * 返回在多个属性上一致的记录构成的数组
   * @param array 源数组
   * @param record 样本记录，对象，数组中的单个元素
   * @param fields 属性构成的字符串数组
   */
  getSames(array:any[],record:any,fields:string[]):any[]{
    let ret = [];
    let sample = this.combineAttr2String(record,fields);
    array.forEach(ele => {
      let detected = this.combineAttr2String(ele,fields);
      if( detected === sample ) ret.push(ele);
    });

    return ret;
  }

  /**
   * 构建在指定的属性上不重复的元素构成的新数组，多个重复的元素中只取第一条
   * @param array 原始数组
   * @param fields 属性名构成的字符串数组
   */
  buildDistArray(array:any[],fields:string[]):any[]{
    let combinArray = [];
    let ret = [];

    combinArray.push(this.combineAttr2String(array[0],fields));
    ret.push(array[0]);

    array.forEach(ele => {
      let detected = this.combineAttr2String(ele,fields);

      let exists = combinArray.some(
        (item,index,array)=>{
          return item === detected;
        }
      )

      if(!exists) {
        combinArray.push(detected);
        ret.push(ele);
      }

    });

    return ret;
  }

  /**
   * 返回格式为：attr1=value1,attr2=value2 的字符串
   * @param item 拥有N个属性的对象实例或者数组元素
   * @param fields 要链接的属性名称字符串构成的数组
   */
  combineAttr2String(item:any,fields:string[]){
    let ret = '';
    let i = 0;
    for(i=0;i<fields.length;i++){
      ret = ret + fields[i] + '=' + item[fields[i]] + ',';
    }

    let last = ret.substr(ret.length-1,1);
    if( last === ',' ) ret = ret.substr(0,ret.length-1)
    return ret;
  }

  /**
   * 便利数组中的对象，将某个属性构建为逗号间隔的字符串
   * @param arr 某类型的对象构成的数组
   * @param attr 对象中的某个属性
   */
  buildAttrValueString<T>(arr:T[],attr:string):string{
    if( !arr ) return '';
    if( !attr ) return '';

    let ret:string = '';
    arr.forEach(ele => {
      for(let attri in ele){
        if( attri == attr ) ret = ret + ele[attr] +',';
      }
    });

    ret = this.stringUtils.delLastSymbol(ret,',');
    return ret;
  }

  replace<T>(arr:T[],index:number,element:T):T[]{
    let ret:T[] = arr.filter(
      ( item:T,ind:number,a:T[] ) =>{
        return ind !== index;
      }
     )

    ret.splice(index,0,element);
    return ret;
  }

  //#region 将指定位序的元素向前移动一个位置
  movePrevious<T>(arr:T[],index:number):T[]{
    if( !arr || !index ) return null;//位序0的元素无法再向前移动了
    let ele:T[] = arr.splice(index,1);
    let deleted = arr.splice(index-1,0,ele[0]);
    return arr.filter( item => 1===1 );
  }
  //#endregion

  //#region 指定的元素向后移动一个位置
  moveNext<T>(arr:T[],index:number){
    if( !arr || index === arr.length-1 ) return null;//最后一个元素无法再向后移动了
    let ele:T[] = arr.splice(index,1);
    let deleted = arr.splice(index+1,0,ele[0]);
    return arr.filter( item => 1===1 );
  }
  //#endregion

  //#region 指定的元素移动到第一位
  moveFirst<T>(arr:T[],index:number){
    if( !arr || !index ) return null;//位序是0的元素无须再移动到第一的位置上
    let ele:T[] = arr.splice(index,1);
    let deleted = arr.splice(0,0,ele[0]);
    return arr.filter( item => 1===1 );
  }
  //#endregion

  //#region 指定的元素移动到最后一位上
  moveLast<T>(arr:T[],index:number){
    if( !arr || index === arr.length-1 ) return null;//最后一个元素无须再移动到最后一个位置上
    let ele:T[] = arr.splice(index,1);
    let deleted = arr.splice(arr.length-1,0,ele[0]);
    return arr.filter( item => 1===1 );
  }
  //#endregion
}
